/**
 *  功能：ESP8266 ch340 DS18B20 温度监控物联网配置
 *  作者：tuut
 *  时间：2023-11-28
 *  描述：
 *      1.
 *      2.
 *      3.
*/
#include <Arduino.h>

#include <ESP8266WiFi.h>

//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
//for 支持Json数据格式的头文件
#include <ArduinoJson.h>
//for 阿里云SDK的头文件
#include <AliyunIoTSDK.h>
//for 发布/订阅功能的头文件
#include <PubSubClient.h>

//for LED status
//Ticker是ESP32 arduino的自带库，可以设置以固定时间间隔反复执行某个任务，特别适合以固定频率从传感器收集数据
#include <Ticker.h>
Ticker ticker;
//for 数据按钮 --------------------------------------------------------------------------------
//for BUTTON status
#include <OneButton.h>
// Reset Button ------------------------------------------------------------------------------
#define resetButtonPin 13
OneButton button(resetButtonPin, true);
// ESP8266 -----------------------------------------------------------------------------------
void initSystem();
// DS18B20 -----------------------------------------------------------------------------------
#include "src/Sketch_oct31b_ds18b20.h"
Sketch_oct31b_ds18b20 ds18b20;
// TDS -----------------------------------------------------------------------------------
#include "src/Sketch_oct31b_tds.h"
Sketch_oct31b_tds tdsDev = Sketch_oct31b_tds();
// Esp8266 WiFi -------------------------------------------------------------------------------
void initWifiManager();
void configModeCallback(WiFiManager *myWiFiManager);
void saveConfigCallback();
void doWiFiTick();
// Aliyun Mqtt -------------------------------------------------------------------------------
#include "src/Sketch_oct31b_aliyun.h"
Sketch_oct31b_aliyun aliyun = Sketch_oct31b_aliyun();
// void initAliyunManager();
// void powerCallback(JsonVariant p);
// OTHER -------------------------------------------------------------------------------------
void tick();
void onButtonClick();
void doTdsTick();
void doDS18B20Tick();
void doAliyun();
// -------------------------------------------------------------------------------------------
static WiFiClient espClient;
//flag for saving data
bool shouldSaveConfig = false;
// 结构体 存储到eeprom
struct device_config {
  char ssid[20];
  char psw[20];
  uint8_t magic;
} config;
// -------------------------------------------------------------------------------------------
struct monitor_data {
  char currentTemperatureA[20] = "-1";  // 当前温度
  char currentTemperatureB[20] = "-1";
  char currentHumidity[20] = "101";  // 当前湿度
  char ch2o[20] = "500";             // 二氧化碳浓度
  char tds[20] = "1000";             // 当前液体浑浊度
} monitor;
/****************************************set up and loop part*********************************/
static long interval_sends = 60;
static long _interval = interval_sends * 1000;
unsigned long l_delayStart = 0;
bool delayRunning = false;  // true if still waiting for delay to finish
void setup() {
  delay(2000);
  l_delayStart = millis();
  delayRunning = true;  // not finished yet
  // 绑定初始化配置参数按钮行为
  button.attachDoubleClick(onButtonClick);
  // 初始化系统配置
  initSystem();
  // 初始化无线网络配置
  initWifiManager();
  // 停止定时器
  ticker.detach();
  // 提示灯常亮，表示网络已经正常链接
  digitalWrite(BUILTIN_LED, LOW);
  ds18b20 = Sketch_oct31b_ds18b20();
  // 初始化DS18B20设备
  ds18b20.initDS18b60DevManager();
  // 初始化TDS检测设备
  // tdsDev.initSEN0244DevManager();
  // 初始化阿里云配置
  // aliyun.initAliyunManager(espClient);
}

void loop() {
  // ESP.wdtFeed();
  // button.tick();
  // doWiFiTick();
  // Serial.println("");
  // doDS18B20Tick();
  // doTdsTick();
  // // 阿里云iOT云数据推送
  // if (WiFi.status() == WL_CONNECTED) {
  //   ticker.attach(0.5, tick);
  //   // digitalWrite(BUILTIN_LED, HIGH);  // turn led off
  //   if (delayRunning && (millis() - l_delayStart) >= _interval) {
  //     delayRunning = false;  // prevent this code being run more then once
  //     doAliyun();
  //     delayRunning = true;
  //     l_delayStart = millis();
  //   }
  // }
}

// -------------------------------------------------------------------------------------------
/**
 * 功能描述：初始化esp8266
 */
void initSystem() {
  Serial.begin(115200);
  Serial.println("Init System");
  for (int i = 0; i < 5; i++) {
    Serial.print(".");
  }
  // 设定LED PIN角为输出角
  pinMode(BUILTIN_LED, OUTPUT);
  // 起始配置WiFi线路AP模式下配置网络。设定LED灯快速闪烁
  ticker.attach(0.6, tick);
}
// -------------------------------------------------------------------------------------------
/**
 * 功能描述：初始化wifimanager
 */
void initWifiManager() {
  /***  步骤一：创建 wifimanager对象 **/
  WiFiManager wifiManager;
  /*************************************/
  /*** 步骤二：进行一系列配置，参考配置类方法 **/
  // 重置保存的修改 目标是为了每次进来都是去掉配置页面
  // wifiManager.resetSettings();
  // 配置连接超时
  wifiManager.setConnectTimeout(60);
  // 打印调试内容
  wifiManager.setDebugOutput(true);
  // 设置最小信号强度
  wifiManager.setMinimumSignalQuality(30);
  // 设置固定AP信息
  IPAddress _ip = IPAddress(192, 168, 4, 25);
  IPAddress _gw = IPAddress(192, 168, 4, 1);
  IPAddress _sn = IPAddress(255, 255, 255, 0);
  wifiManager.setAPStaticIPConfig(_ip, _gw, _sn);
  // 设置进入AP模式的回调
  wifiManager.setAPCallback(configModeCallback);
  // 设置点击保存的回调
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  // 设置 如果配置错误的ssid或者密码 退出配置模式
  wifiManager.setBreakAfterConfig(true);
  // 设置过滤重复的AP 默认可以不用调用 这里只是示范
  wifiManager.setRemoveDuplicateAPs(true);
  /*************************************/
  /*** 步骤三：尝试连接网络，失败去到配置页面 **/
  String mac = WiFi.macAddress();
  mac.replace(":", "");
  String ssid = String("ESP8266-") + "-" + mac;
  if (!wifiManager.autoConnect(ssid.c_str(), "esp8266-tuut")) {
    Serial.println("failed to connect and hit timeout");
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(1000);
  }
  /*************************************/
  // 读取配置页面配置好的信息
  strcpy(config.ssid, wifiManager.getWiFiSSID().c_str());
  strcpy(config.psw, wifiManager.getWiFiPass().c_str());
  // 保存自定义信息
  if (shouldSaveConfig) {
    Serial.println("saving config");
    const size_t capacity = JSON_ARRAY_SIZE(1) + 2 * JSON_OBJECT_SIZE(4) + 60;
    DynamicJsonDocument doc(capacity);
    JsonObject root = doc.to<JsonObject>();
    root["ssid"] = config.ssid;
    root["psw"] = config.psw;
    serializeJson(root, Serial);
    //end save
  }
  Serial.println("");
  Serial.println("load Local IP Address:");
  Serial.print(WiFi.localIP());
  Serial.println("Connected...So Easy :)");
}

/**
 * 功能描述：配置进入AP模式通知回调
 */
void configModeCallback(WiFiManager *myWiFiManager) {
  Serial.println("Entered config mode");
  Serial.println(WiFi.softAPIP());
  //if you used auto generated SSID, print it
  Serial.println(myWiFiManager->getConfigPortalSSID());
  //entered config mode, make led toggle faster
  ticker.attach(0.2, tick);
}

/**
 * 功能描述：设置点击保存的回调
 */
void saveConfigCallback() {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

/**
 * 功能：连接路由心跳函数
 */
void doWiFiTick() {
  static bool taskStarted = false;
  static bool startSTAFlag = false;
  static uint32_t lastWiFiCheckTick = 0;
  if (!startSTAFlag) {
    startSTAFlag = true;
    Serial.print("connect to ap:");
    Serial.println(config.ssid);
    WiFi.disconnect();
    WiFi.mode(WIFI_STA);
    WiFi.begin(config.ssid, config.psw);
  }
  //未连接1s重连
  if (WiFi.status() != WL_CONNECTED) {
    if (millis() - lastWiFiCheckTick > 1000) {
      lastWiFiCheckTick = millis();
      Serial.print(".");
    }
  }
  //连接成功建立
  else {
    if (taskStarted == false) {
      taskStarted = true;
      Serial.print("\r\nGet IP Address: ");
      Serial.println(WiFi.localIP());
    }
  }
  ESP.wdtFeed();
}
// -------------------------------------------------------------------------------------------
void doAliyun() {
  // #include <JsonStreamingParser.h>
  // JsonObject& root = jsonBuffer.createObject();
  const size_t capacity = JSON_ARRAY_SIZE(1) + 2 * JSON_OBJECT_SIZE(6) + 60;
  DynamicJsonDocument doc(capacity);
  JsonObject root = doc.to<JsonObject>();
  root["inSideCurrentTemperature"] = monitor.currentTemperatureA;
  root["outSideCurrentTemperature"] = monitor.currentTemperatureB;
  root["currentHumidity"] = monitor.currentHumidity;
  root["ch2o"] = monitor.ch2o;
  root["tds"] = monitor.tds;
  String str_root;
  serializeJson(root, str_root);
  aliyun.upload2Aliyun(str_root, _interval);
}
void doTdsTick() {
  float _tmp = ds18b20.getDs18b20Temp();
  tdsDev.setDs18b20Temperature(_tmp);
  tdsDev.loadTDSAPI(_interval);
  strcpy(monitor.tds, tdsDev.getTds().c_str());
}
void doDS18B20Tick() {
  ds18b20.loadDS18B20API(_interval);
  strcpy(monitor.currentTemperatureA, ds18b20.str_insideTemp.c_str());
  strcpy(monitor.currentTemperatureB, ds18b20.str_outsideTemp.c_str());
}
// -------------------------------------------------------------------------------------------
/**
 * 功能描述：设置LED灯闪烁，提示用户进入配置模式
 */
void tick() {
  //toggle state
  int state = digitalRead(BUILTIN_LED);  // get the current state of GPIO1 pin
  digitalWrite(BUILTIN_LED, !state);     // set pin to the opposite state
}
// -------------------------------------------------------------------------------------------
// 重置
void onButtonClick() {
  Serial.println(F("Button  click."));
  delay(1000);
}
